#include <fnmatch.h>

#include "servlet.h"
#include "qtch/log.h"


namespace qtch {
namespace http {

int32_t FunctionServlet::handle(HttpRequest::ptr request
                    , HttpResponse::ptr response
                    , HttpSession::ptr session) {
    return m_cb(request,response,session);
}

FunctionServlet::FunctionServlet(callback cb)
    :Servlet("FunctionServlet")
    ,m_cb(cb){
}

ServletDispath::ServletDispath() 
    :Servlet("ServletDispath"){
    m_default.reset(new NotFoundServlet("qtch/1.0.0"));
}

ServletDispath::~ServletDispath() {
    m_datas.clear();
    m_globs.clear();
}

int32_t ServletDispath::handle(HttpRequest::ptr request
                , HttpResponse::ptr response
                , HttpSession::ptr session){
    Servlet::ptr slt = getMatchedServlet(request->getPath());
    if(slt){
        return slt->handle(request,response,session);
    }
    return -1;
}
void ServletDispath::addServlet(const std::string& uri, Servlet::ptr slt){
    addServlet(uri, std::make_shared<HoldServletCreator>(slt));
}

void ServletDispath::addServlet(const std::string& uri, FunctionServlet::callback cb){
    addServlet(uri, std::make_shared<FunctionServlet>(cb));
}

void ServletDispath::addGlobServlet(const std::string& uri, Servlet::ptr slt){
    addGlobServlet(uri,std::make_shared<HoldServletCreator>(slt));

}

void ServletDispath::addGlobServlet(const std::string& uri, FunctionServlet::callback cb){
    addGlobServlet(uri, std::make_shared<FunctionServlet>(cb));
}

void ServletDispath::addServlet(const std::string& uri, IServletCreator::ptr creator){
    RWMutexType::WriteLock lock(m_mutex);
    m_datas[uri] = creator;
}

void ServletDispath::addGlobServlet(const std::string& uri, IServletCreator::ptr creator){
    RWMutexType::WriteLock lock(m_mutex);
    for(auto it = m_globs.begin(); it != m_globs.end(); ++it){
        if(it->first == uri){
            m_globs.erase(it);
            break;
        }
    }
    m_globs.push_back(std::make_pair(uri, creator));
}

void ServletDispath::delServlet(const std::string& uri){
    RWMutexType::WriteLock lock(m_mutex);
    m_datas.erase(uri);
}

void ServletDispath::delGlobServlet(const std::string& uri){
    RWMutexType::WriteLock lock(m_mutex);
    for(auto it = m_globs.begin(); it != m_globs.end(); ++it){
        if(it->first == uri){
            m_globs.erase(it);
            break;
        }
    }
}

void ServletDispath::listAllServletCreator(std::unordered_map<std::string, IServletCreator::ptr>& infos){
    RWMutexType::ReadLock lock(m_mutex);
    for(auto& i : m_datas){
        infos[i.first] = i.second;
    }
}

void ServletDispath::listAllGlobServletCreator(std::unordered_map<std::string, IServletCreator::ptr>& infos){
    RWMutexType::ReadLock lock(m_mutex);
    for(auto& i : m_globs){
        infos[i.first] = i.second;
    }
}


Servlet::ptr ServletDispath::getServlet(const std::string& uri) {
    RWMutexType::ReadLock lock(m_mutex);
    auto it = m_datas.find(uri);
    if(it != m_datas.end()){
        return it->second->get();
    }
    return nullptr;
}

Servlet::ptr ServletDispath::getGlobServlet(const std::string& uri) {
    RWMutexType::ReadLock lock(m_mutex);
    for(auto it = m_globs.begin(); it != m_globs.end(); ++it){
        if(it->first == uri){
            return it->second->get();
        }
    }
    return nullptr;
}

Servlet::ptr ServletDispath::getMatchedServlet(const std::string& uri) {
    RWMutexType::ReadLock lock(m_mutex);
    auto mit = m_datas.find(uri);
    if(mit != m_datas.end()){
        return mit->second->get();
    }
    for(auto it = m_globs.begin(); it != m_globs.end(); ++it){
        if(!fnmatch(it->first.c_str(),uri.c_str(),0)){
            return it->second->get();
        }
    }
    return m_default;
}

NotFoundServlet::NotFoundServlet(const std::string& name)
    :Servlet("NotFoundServlet"){
    m_name = name;
    m_content = "<html><head><title>404 Not Found"
        "</title></head><body><center><h1>404 Not Found</h1></center>"
        "<hr><center>" + name + "</center></body></html>";
}

int32_t NotFoundServlet::handle(HttpRequest::ptr request
            , HttpResponse::ptr response
            , HttpSession::ptr session){
    response->setStatus(HttpStatus::NOT_FOUND);
    response->setBody(m_content);
    response->setHeader("Content-Type","text/html");
    return 0;
}





}
}